package com.biz.primus.ms.tms.service;

import com.biz.primus.base.constant.DeliveryOrderStreamConstant;
import com.biz.primus.base.enums.CommonStatus;
import com.biz.primus.base.exception.BizBusinessException;
import com.biz.primus.base.exception.utils.AssertUtils;
import com.biz.primus.base.vo.push.Notification;
import com.biz.primus.common.utils.CopyUtils;
import com.biz.primus.common.utils.DateUtil;
import com.biz.primus.common.vo.SpringPageVO;
import com.biz.primus.model.depot.vo.EmployeeVo;
import com.biz.primus.model.tms.builder.DeliveryDetailBuilder;
import com.biz.primus.model.tms.enums.DeliveryOrderStatus;
import com.biz.primus.model.tms.exception.TmsExceptions;
import com.biz.primus.model.tms.vo.*;
import com.biz.primus.ms.base.service.AbstractBaseService;
import com.biz.primus.ms.base.service.SequenceGenerator;
import com.biz.primus.ms.tms.dao.po.DeliveryOrderPo;
import com.biz.primus.ms.tms.dao.po.DeliverySignItemPo;
import com.biz.primus.ms.tms.dao.repository.DeliverOrderRepository;
import com.biz.primus.ms.tms.feign.EmployeeFeignClient;
import com.biz.primus.ms.tms.feign.OmsOrderFeignClient;
import com.biz.primus.ms.tms.specification.DeliverOrderListSpecification;
import com.biz.primus.ms.tms.specification.DeliveryOrderPageSpecification;
import com.biz.primus.ms.tms.stream.DeliverOrderSource;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.stream.annotation.StreamListener;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.stream.Collectors;

import static com.google.common.collect.Lists.newArrayList;

/**
 * DeliveryService
 * 配送单服务
 *
 * @author JKLiues
 * @date 2017/11/21
 */
@Service
@Slf4j
public class DeliveryOrderService extends AbstractBaseService {

    @Autowired
    private DeliverOrderRepository deliverOrderRepository;
    @Autowired
    private EmployeeFeignClient employeeFeignClient;
    @Autowired
    private OmsOrderFeignClient omsOrderFeignClient;
    @Autowired
    private SequenceGenerator sequenceGenerator;
    @Autowired
    private DeliverOrderSource deliverOrderSource;


    /**
     * 查询配送单列表（"待配送,配送中,已完成,已评价"）
     *
     * @param reqVo 列表请求VO
     */
    public List<DeliveryOrderVo> getDeliveryOrder(DeliverOrderListReqVo reqVo) {
        DeliveryOrderStatus deliveryStatus = reqVo.getDeliveryStatus();
        AssertUtils.notNull(deliveryStatus, TmsExceptions.DELIVER_ORDER_STATUS_IS_NULL);
        Long empId = reqVo.getEmpId();
        EmployeeVo employee = employeeFeignClient.findByEmployeeId(empId);
        String employeeCode = employee.getEmployeeCode();
        String depotCode = employee.getDepotCode();
        Sort sort = new Sort(Sort.Direction.ASC, "createTimestamp");
        PageRequest pageRequest = reqVo.buildPageRequest(sort);
        DeliverOrderListSpecification specification = new DeliverOrderListSpecification();
        switch (deliveryStatus) {
            case WAIT_BE_SINGLE:
                if (StringUtils.isBlank(depotCode)) {
                    log.warn("员工{}当前时间{}无绑定门店", depotCode, DateUtil.getNowDate());
                    return Lists.newArrayList();
                }
                specification.setDepotCode(depotCode);
                specification.addDeliveryStatus(DeliveryOrderStatus.WAIT_BE_SINGLE);
                specification.addDeliveryStatus(DeliveryOrderStatus.DELAY_SIGNED);
                break;
            case DELIVERING:
                specification.setEmpCode(employeeCode);
                specification.addDeliveryStatus(DeliveryOrderStatus.ARRIVED);
                specification.addDeliveryStatus(DeliveryOrderStatus.DELIVERING);
                break;
            case FINISH:
                specification.setEmpCode(employeeCode);
                specification.addDeliveryStatus(DeliveryOrderStatus.FINISH);
                break;
            case EVALUATE:
                specification.setEmpCode(employeeCode);
                specification.addDeliveryStatus(DeliveryOrderStatus.EVALUATE);
                break;
            default:
                log.error("接受到非法配送单查询{}", reqVo);
                throw new BizBusinessException(TmsExceptions.DELIVER_ORDER_STATUS_NOT_SUPPORT);
        }
        Page<DeliveryOrderPo> deliverOrders = deliverOrderRepository.findAll(specification, pageRequest);
        return buildOrderInfo(deliverOrders.getContent());
    }

    /**
     * 获取订单数量
     *
     * @param reqVo reqVo请求
     */
    public List<OrderNumVo> getOrderNum(UserIdReqVo reqVo) {
        Long userId = reqVo.getUserId();
        String empCode = employeeFeignClient.getCodeById(userId);
        List<OrderNumVo> result = Lists.newArrayList();
        //待配送
        Integer count1 = deliverOrderRepository.findCount(Lists.newArrayList(DeliveryOrderStatus.DELAY_SIGNED, DeliveryOrderStatus.WAIT_BE_SINGLE));
        result.add(new OrderNumVo(count1, "待配送"));

        //配送中
        Integer count2 = deliverOrderRepository.findCount(empCode, Lists.newArrayList(DeliveryOrderStatus.DELIVERING, DeliveryOrderStatus.ARRIVED));
        result.add(new OrderNumVo(count2, "配送中"));

        //配送完成
        Integer count3 = deliverOrderRepository.findCount(empCode, Lists.newArrayList(DeliveryOrderStatus.FINISH));
        result.add(new OrderNumVo(count3, "配送完成"));

        //配送完成
        Integer count4 = deliverOrderRepository.findCount(empCode, Lists.newArrayList(DeliveryOrderStatus.EVALUATE));
        result.add(new OrderNumVo(count4, "已评价"));
        return result;
    }

    /**
     * 获取应签收商品及数量
     */
    public List<OrderInfoItemRespVo> getSignProducts(BaseTmsReqVo reqVo) {
        Long userId = reqVo.getUserId();
        String empCode = employeeFeignClient.getCodeById(userId);
        String deliveryCode = reqVo.getDeliveryCode();
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.isTrue(Objects.equals(empCode, deliveryOrder.getDeliveryUserNo()), TmsExceptions.OPERATOR_NOT_ALLOWED);
        List<SimpleOrderInfoRespVo> orderInfo = omsOrderFeignClient.findOrderInfo(Lists.newArrayList(deliveryOrder.getOrderCode()));
        AssertUtils.notEmpty(orderInfo, TmsExceptions.ORDER_NOT_FOUNT);
        SimpleOrderInfoRespVo orderInfoRespVo = orderInfo.get(0);
        return orderInfoRespVo.getProductItems();
    }

    /**
     * 获取已签收配送单签收商品及数量
     */
    public OrderSignInfoRespVo getSignedInfo(BaseTmsReqVo reqVo) {
        Long userId = reqVo.getUserId();
        String empCode = employeeFeignClient.getCodeById(userId);
        String deliveryCode = reqVo.getDeliveryCode();
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findByDeliveryCode(deliveryCode);
        AssertUtils.isTrue(Objects.equals(empCode, deliveryOrder.getDeliveryUserNo()), TmsExceptions.OPERATOR_NOT_ALLOWED);
        List<SimpleOrderInfoRespVo> orderInfo = omsOrderFeignClient.findOrderInfo(Lists.newArrayList(deliveryOrder.getOrderCode()));
        AssertUtils.notEmpty(orderInfo, TmsExceptions.ORDER_NOT_FOUNT);
        SimpleOrderInfoRespVo orderInfoRespVo = orderInfo.get(0);
        return this.buildSignedInfo(deliveryOrder, orderInfoRespVo);
    }

    /**
     * 获取商品订单价格
     */
    public OrderPriceRespVo getOrderPrice(OrderPriceReqVo reqVo) {
        return omsOrderFeignClient.getOrderPrice(reqVo);
    }

    /**
     * 后台获取配送单详情
     *
     * @param deliveryId 配送单ID
     * @return DeliveryDetailRespVo
     */
    public DeliveryDetailRespVo getDeliveryDetail(Long deliveryId) {
        AssertUtils.notNull(deliveryId, TmsExceptions.DELIVER_ORDER_CODE_IS_NULL);
        DeliveryOrderPo deliveryOrder = deliverOrderRepository.findOne(deliveryId);
        AssertUtils.notNull(deliveryOrder, TmsExceptions.DELIVER_ORDER_NOT_EXIST);
        OrderInfoRespVo orderInfo =
                omsOrderFeignClient.findOrderDetailInfo(deliveryOrder.getOrderCode());
        AssertUtils.notNull(orderInfo, TmsExceptions.ORDER_NOT_FOUNT);
        DeliveryDetailRespVo respVo = new DeliveryDetailRespVo();
        CopyUtils.copyProperties(orderInfo, respVo);
        return this.buildDeliveryDetail(deliveryOrder, orderInfo);
    }

    /**
     * 后台根据条件查询配送单
     *
     * @param reqVo 查询条件
     * @return SpringPageVO
     */
    public SpringPageVO<DeliverySearchRespVo> searchPageDelivery(DeliverySearchReqVo reqVo) {
        PageRequest pageable = new PageRequest(Math.max(0, reqVo.getPage() - 1), reqVo.getSize(),
                Sort.Direction.DESC, "createTimestamp");
        Page<DeliveryOrderPo> page = deliverOrderRepository.findAll(new DeliveryOrderPageSpecification(reqVo), pageable);
        List<DeliveryOrderPo> content = page.getContent();
        List<DeliverySearchRespVo> deliveryOrderVos = this.buildSearchResponse(content);
        return new SpringPageVO<>(deliveryOrderVos, page.getTotalElements(), reqVo.getSize(), reqVo.getPage());
    }

    /**
     * 根据entity,开始时间，结束时间查询轨迹 2017-02-18 10:11:52
     * 鹰眼轨迹
     */
    public List<LongitudeAndLatitudeVo> getMaps(String entityName, String startDate, String endDate) {
        try {
            Long startTime;
            Long endTime;
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
            if (StringUtils.isBlank(startDate)) {
                return newArrayList();
            }
            startTime = dateFormat.parse(startDate).getTime();
            if (StringUtils.isNotBlank(endDate)) {
                endTime = dateFormat.parse(endDate).getTime();
            } else {
                endTime = System.currentTimeMillis();
            }
            if (endTime - startTime > 86400000) {
                startTime = endTime - 86000000;
            }
            return YingyanUtil.getMapUrl(entityName, startTime, endTime);
        } catch (UnsupportedEncodingException e) {
            log.info("查询轨迹数据异常");
            e.printStackTrace();
        } catch (ParseException e) {
            log.info("转换时间异常");
            e.printStackTrace();
        }
        return newArrayList();
    }


    @Transactional(rollbackFor = Exception.class)
    @StreamListener(DeliveryOrderStreamConstant.RECEIVE_ORDER)
    public void receivedOmsOrder(OmsOrder2TmsMsgVo msgVo) {
        String depotCode = msgVo.getDepotCode();
        String orderCode = msgVo.getOrderCode();
        Boolean cancel = msgVo.getCancel();

        if (StringUtils.isBlank(orderCode)) {
            log.error("接收到OMS推送配送订单,订单号为空{}", msgVo);
            return;
        }
        if (cancel) {
            log.info("接收到OMS推送取消订单配送订单,订单号{}", orderCode);
            List<DeliveryOrderPo> deliveryOrders = deliverOrderRepository.findByOrderCode(orderCode);
            if (CollectionUtils.isEmpty(deliveryOrders)) {
                log.error("订单{}在配送系统不存在,取消失败");
                return;
            }
            DeliveryOrderPo deliveryOrderPo = deliveryOrders.get(0);
            deliveryOrderPo.setStatus(CommonStatus.DISABLE);
//            todo:推送消息至app JKLiues
//            this.pushToApp();
            return;
        }
        if (StringUtils.isBlank(depotCode)) {
            log.error("接收到OMS推送配送订单,门店号为空{}", msgVo);
        }
        DeliveryOrderPo deliveryOrder = new DeliveryOrderPo();
        deliveryOrder.setDeliveryCode(depotCode);
        deliveryOrder.setOrderCode(orderCode);
        deliveryOrder.setDepotName(msgVo.getDepotName());
        deliveryOrder.setId(idService.getNextId());
        deliveryOrder.setDeliveryCode(sequenceGenerator.generateNextCode(SequenceGenerator.CodeType.tms));
        deliverOrderRepository.save(deliveryOrder);
    }

    /**
     * 推送消息至APP
     *
     * @param notification 消息体
     */
    private void pushToApp(Notification notification) {
        deliverOrderSource.push().send(MessageBuilder.withPayload(notification).build());
    }

    /**
     * 构建rest配送单列表
     *
     * @param deliveryOrders 配送单
     * @return List
     */
    private List<DeliveryOrderVo> buildOrderInfo(List<DeliveryOrderPo> deliveryOrders) {
        if (CollectionUtils.isEmpty(deliveryOrders)) {
            return Lists.newArrayList();
        }
        List<String> orderCodes = deliveryOrders.stream().map(DeliveryOrderPo::getOrderCode).collect(Collectors.toList());
        Map<String, DeliveryOrderPo> map = deliveryOrders.stream().collect(Collectors.toMap(DeliveryOrderPo::getOrderCode, po -> po));
        List<SimpleOrderInfoRespVo> orderInfo = omsOrderFeignClient.findOrderInfo(orderCodes);
        if (CollectionUtils.isEmpty(orderInfo)) {
            return Lists.newArrayList();
        }
        return orderInfo.stream()
                .map(order -> {
                    DeliveryOrderVo orderVo = new DeliveryOrderVo();
                    CopyUtils.copyProperties(order, orderVo);
                    DeliveryOrderPo deliveryOrder = map.get(order.getOrderCode());
                    orderVo.setDeliveryOrderCode(deliveryOrder.getDeliveryCode());
                    orderVo.setDeliveryStatus(deliveryOrder.getDeliveryStatus());
                    return orderVo;
                })
                .collect(Collectors.toList());
    }

    /**
     * 构建manage配送单列表
     *
     * @param deliveryOrders 配送单
     * @return List
     */
    private List<DeliverySearchRespVo> buildSearchResponse(List<DeliveryOrderPo> deliveryOrders) {
        if (CollectionUtils.isEmpty(deliveryOrders)) {
            return Lists.newArrayList();
        }
        List<String> orderCodes = deliveryOrders.stream().map(DeliveryOrderPo::getOrderCode).collect(Collectors.toList());
        Map<String, DeliveryOrderPo> map = deliveryOrders.stream().collect(Collectors.toMap(DeliveryOrderPo::getOrderCode, po -> po));
        List<SimpleOrderInfoRespVo> orderInfo = omsOrderFeignClient.findOrderInfo(orderCodes);
        if (CollectionUtils.isEmpty(orderInfo)) {
            return Lists.newArrayList();
        }
        return orderInfo.stream().map(
                info -> {
                    String orderCode = info.getOrderCode();
                    DeliveryOrderPo deliveryOrder = map.get(orderCode);
                    DeliverySearchRespVo respVo = new DeliverySearchRespVo();
                    CopyUtils.copyProperties(info, respVo);
                    CopyUtils.copyProperties(deliveryOrder, respVo);
                    if (deliveryOrder.getOrderTakeTime() != null) {
                        respVo.setOrderTakeLocation(String.format("(%s,%s)", deliveryOrder.getOrderTakeLon(), deliveryOrder.getOrderTakeLat()));
                    }
                    if (deliveryOrder.getStartTime() != null) {
                        respVo.setStartLocation(String.format("(%s,%s)", deliveryOrder.getStartLon(), deliveryOrder.getStartLat()));
                    }
                    if (deliveryOrder.getArriveTime() != null) {
                        respVo.setArriveLocation(String.format("(%s,%s)", deliveryOrder.getArriveLon(), deliveryOrder.getArriveLat()));
                    }
                    if (deliveryOrder.getCompleteTime() != null) {
                        respVo.setCompleteLocation(String.format("(%s,%s)", deliveryOrder.getCompleteLon(), deliveryOrder.getCompleteLat()));
                    }
                    return respVo;
                }
        ).collect(Collectors.toList());
    }

    private DeliveryDetailRespVo buildDeliveryDetail(DeliveryOrderPo deliveryOrder, OrderInfoRespVo orderInfoResp) {
        if (deliveryOrder == null || orderInfoResp == null) {
            return new DeliveryDetailRespVo();
        }
        DeliveryDetailBuilder builder = new DeliveryDetailBuilder();
        OrderInfoVo orderInfo = new OrderInfoVo();

        List<OrderInfoItemRespVo> productItems = orderInfoResp.getProductItems();
        List<OrderFreeInfo> freeInfo = orderInfoResp.getFreeInfo();
        PayInfoVo payInfo = this.getPayInfo(orderInfoResp);
        ConsigneeInfoVo consigneeInfo = orderInfoResp.getConsigneeInfo();
        MemberInfoVo memberInfo = orderInfoResp.getMemberInfo();
        DeliveryInfoVo deliveryInfo = this.buildDeliverInfo(deliveryOrder);
        List<DeliveryStatusInfo> deliveryStatusInfo = this.buildDeliverStatusInfo(deliveryOrder);
        OrderSignInfoRespVo signInfo = this.buildSignedInfo(deliveryOrder, orderInfoResp);
        List<SplitOrderItemVo> splitOrderItem = orderInfoResp.getSplitItems();
        builder.orderInfo(orderInfo);
        builder.splitItems(splitOrderItem);
        builder.orderItems(productItems);
        builder.freeInfo(freeInfo);
        builder.payInfo(payInfo);
        builder.consigneeInfo(consigneeInfo);
        builder.memberInfo(memberInfo);
        builder.deliveryInfo(deliveryInfo);
        builder.deliverStatusInfo(deliveryStatusInfo);
        builder.signInfo(signInfo);
        return builder.build();
    }

    private OrderSignInfoRespVo buildSignedInfo(DeliveryOrderPo deliveryOrder, SimpleOrderInfoRespVo orderInfoRespVo) {
        OrderSignInfoRespVo respVo = new OrderSignInfoRespVo();
        Integer paidAmount = orderInfoRespVo.getPaidAmount();
        String payType = orderInfoRespVo.getPayType();
        respVo.setPaidAmount(paidAmount);
        respVo.setPaymentType(payType);
        respVo.setCompleteTime(deliveryOrder.getCompleteTime());
        respVo.setSignType(deliveryOrder.getSignType());
        List<DeliverySignItemPo> signItemPos = deliveryOrder.getDeliveryPartSignItems();
        List<OrderSignItemRespVo> signItems = Optional.of(signItemPos).orElse(Lists.newArrayList())
                .stream()
                .map(po -> {
                    OrderSignItemRespVo signItem = new OrderSignItemRespVo();
                    signItem.setProductName(po.getProductName());
                    signItem.setSignQuantity(po.getSignQuantity());
                    return signItem;
                }).collect(Collectors.toList());
        respVo.setSignItems(signItems);
        return respVo;
    }

    private List<DeliveryStatusInfo> buildDeliverStatusInfo(DeliveryOrderPo deliveryOrder) {
        List<DeliveryStatusInfo> info = Lists.newArrayList();
        if (deliveryOrder.getOrderTakeTime() != null) {
            DeliveryStatusInfo itemInfo = new DeliveryStatusInfo();
            itemInfo.setStatusText("接单");
            itemInfo.setTime(deliveryOrder.getOrderTakeTime());
            itemInfo.setLocation(String.format("(%s,%s)", deliveryOrder.getOrderTakeLon(), deliveryOrder.getOrderTakeLat()));
            info.add(itemInfo);
        }
        if (deliveryOrder.getStartTime() != null) {
            DeliveryStatusInfo itemInfo = new DeliveryStatusInfo();
            itemInfo.setStatusText("出发");
            itemInfo.setTime(deliveryOrder.getStartTime());
            itemInfo.setLocation(String.format("(%s,%s)", deliveryOrder.getStartLon(), deliveryOrder.getStartLat()));
            info.add(itemInfo);
        }
        if (deliveryOrder.getArriveTime() != null) {
            DeliveryStatusInfo itemInfo = new DeliveryStatusInfo();
            itemInfo.setStatusText("到达");
            itemInfo.setTime(deliveryOrder.getArriveTime());
            itemInfo.setLocation(String.format("(%s,%s)", deliveryOrder.getArriveLon(), deliveryOrder.getArriveLat()));
            info.add(itemInfo);
        }
        if (deliveryOrder.getCompleteTime() != null) {
            DeliveryStatusInfo itemInfo = new DeliveryStatusInfo();
            itemInfo.setStatusText("签收");
            itemInfo.setTime(deliveryOrder.getCompleteTime());
            itemInfo.setLocation(String.format("(%s,%s)", deliveryOrder.getCompleteLon(), deliveryOrder.getCompleteLat()));
            info.add(itemInfo);
        }
        return info;
    }

    private DeliveryInfoVo buildDeliverInfo(DeliveryOrderPo deliveryOrder) {
        DeliveryInfoVo deliveryInfo = new DeliveryInfoVo();
        CopyUtils.copyProperties(deliveryOrder, deliveryInfo);
        return deliveryInfo;
    }

    private PayInfoVo getPayInfo(OrderInfoRespVo orderInfoResp) {
        PayInfoVo payInfo = new PayInfoVo();
        CopyUtils.copyProperties(orderInfoResp, payInfo);
        return payInfo;
    }
}
